import { director } from "cc";
import { CCFloat } from "cc";
import { Component, Node, Prefab, ScrollView, UITransform, Vec2, _decorator, error, instantiate, isValid, log, v2, v3 } from "cc";
import { EDITOR } from "cc/env";
const { ccclass, property, menu } = _decorator;

// let createFlag = 0;

/**
 * 循环+分帧滑动面板
 */
@ccclass('RecycleScroll')
@menu("性能优化/RecycleScroll")
export default class RecycleScroll extends Component {
    /** item预制 */
    @property(Prefab)
    itemPrefab: Prefab = null;

    /** item间隔 */
    @property(Vec2)
    spacing: Vec2 = v2();

    /** 上下间隔 */
    @property(CCFloat)
    paddingTop: number = 0;

    /** 子节点缩放 */
    @property(CCFloat)
    itemScale: number = 1;

    /** item数量 */
    private _numItems: number = 0;
    public get numItems() {
        return this._numItems;
    }
    public set numItems(value: number) {
        this._numItems = value;
        this._hideAllItems();
        this._initialize();
        this._updateContentHeight();
        this.updateAllItems();
    }

    frameLoadParama = { fun: null, count: 0, index: 0 };

    /** 视图内显示列数 */
    private _viewCol: number = 0;
    /** 视图内显示行数 */
    private _viewRow: number = 0;
    /** 视图窗宽 */
    private _viewW: number = 0;
    /** 视图窗高 */
    private _viewH: number = 0;
    /** item格子宽 */
    private _itemW: number = 0;
    /** item格子高 */
    private _itemH: number = 0;
    /** content上一次y坐标 */
    private _lastPosY: number = 0;
    /** 是否已初始化 */
    private _isInit: boolean = false;
    /** item的index */
    private _itemsUUIDToIndex: { [uuid: string]: number } = {};
    /**index对应的item */
    private _indexToItem: { [index: number]: Node } = {};

    /**是否能选中 */
    private _isSelectable: boolean = false;
    public get isSelectable() {
        return this._isSelectable;
    }
    public set isSelectable(value: boolean) {
        this._isSelectable = value;
    }

    /**选中索引 */
    private _selectedIndex: number = -1;
    public get selectedIndex() {
        return this._selectedIndex;
    }
    public set selectedIndex(value: number) {
        this._selectedIndex = value;
        this.updateAllItems();
    }

    /**能否多选 */
    private _isMultiSelect: boolean = false;
    public get isMultiSelect() {
        return this._isMultiSelect;
    }
    public set isMultiSelect(value: boolean) {
        this._isMultiSelect = value;
    }

    /**选中索引列表 */
    private _selectedIndexes: number[] = [];
    public get selectedIndexes() {
        return this._selectedIndexes;
    }
    public set selectedIndexes(value: number[]) {
        this._selectedIndexes = value;
        this.updateAllItems();
    }
    // private _itemsIndexToNode: { [index: string]: Node } = {};

    private _fleshInterval: number = 0.2;
    private _fleshCounter: number = 0;
    private _initTimer: number = 0.05;
    private _initCounter: number = 0;
    private _isResize: boolean = false;
    private _itemStartPos: Vec2 = v2();
    private _isResizeFinish: boolean = false;
    private _lineIndex: number = -1;
    private _isLongPress: boolean = false;

    public longPressTime: number = 0.5;
    public longPressTimer: number = 0;

    /** item列表 */
    public itemList: Node[] = [];
    /** item父节点 */
    public content: UITransform = null;

    /** item刷新回调 */
    public onItemRender(index: number, node: Node) { }
    /** item点击回调 */
    public onItemLongPress: (index: number, node: Node) => void = null;;
    /** item点击回调 */
    public onItemClicked(index: number, node: Node) { }
    /**选中回调 */
    public onItemSelected: (index: number, node: Node) => void = null;
    /** 清除某选中 */
    public clearSelection(index: number) {
        if (this._isMultiSelect && this._selectedIndexes.indexOf(index) != -1) {
            this._selectedIndexes.splice(this._selectedIndexes.indexOf(index), 1);

            if (this.onItemSelected) {
                this.onItemSelected(index, this._indexToItem[index]);
            }
        }

        if (this._isSelectable && this._selectedIndex == index) {
            this._selectedIndex = -1;

            if (this.onItemSelected) {
                this.onItemSelected(index, this._indexToItem[index]);
            }
        }
    }
    /** 清除所有选中 */
    public clearSelectionAll() {
        this._selectedIndexes = [];
        this._selectedIndex = -1;
        this.updateAllItems();
    }

    /** 刷新所有item */
    public updateAllItems() {
        this.itemList.forEach((item: Node) => this._updateItem(this._itemsUUIDToIndex[item.uuid], item, true));
    }

    public scrollToIndexVertical(index: number, duration: number = 0.2) {
        const scrollComp = this.node.getComponent(ScrollView);
        scrollComp.stopAutoScroll();
        const contentUTF = this._getContentUTF();
        const p = (this._itemH * Math.floor(index / this._viewCol)) / (contentUTF.height - this._viewH);
        scrollComp.scrollToPercentVertical(1 - p, duration);
    }

    /** 使指定索引物品处于可视范围  */
    public scrollItemVisible(index: number, duration: number = 0.2) {
        if (index < 0 || index >= this.numItems) return;
        const scrollView = this.node.getComponent(ScrollView);
        scrollView.stopAutoScroll();
        const itemRow = Math.floor(index / this._viewCol);
        const itemPosY = -this._itemH * (itemRow + 0.5);
        const itemWposy = itemPosY + this.content.node.worldPosition.y;
        const viewWposy = this.content.node.parent.getWorldPosition().y;
        const posH = (this._viewH - this._itemH) >> 1;
        const deltaY = viewWposy - itemWposy;
        if (Math.abs(deltaY) < posH) return;
        const sign = deltaY > 0 ? -1 : 1;

        const offset = scrollView.getScrollOffset().clone();
        offset.y += deltaY + (sign * posH);
        scrollView.scrollToOffset(offset, duration);
    }

    public getItemDirPos(itemIndex: number) {
        const x = (itemIndex % this._viewCol) * this._itemW;
        const y = -Math.floor(itemIndex / this._viewCol) * this._itemH + (this.spacing.y >> 1) - this.paddingTop;
        const contentUTF = this._getContentUTF();
        const wpos = contentUTF.convertToWorldSpaceAR(v3(x, y));
        const parentUTF = this._getContentUTF().node.parent.getComponent(UITransform);
        const itemInViewPos = parentUTF.convertToNodeSpaceAR(wpos);
        let horizon = 0;
        let vertical = 0;
        horizon = itemInViewPos.x < -this._viewW / 2 ? -1 : (itemInViewPos.x > this._viewW / 2 ? 1 : 0);
        vertical = itemInViewPos.y < -this._viewH / 2 ? -1 : (itemInViewPos.y > this._viewH / 2 ? 1 : 0);
        return [horizon, vertical];
    }

    protected onLoad(): void {
        this.node.on(Node.EventType.SIZE_CHANGED, this.onSizeChange, this);
        this._initContentPos();
    }

    protected onDestroy(): void {
        this.node.targetOff(this);
        // screen.off(`window-resize`, this.onWindowResize, this);
    }

    /** 初始化content位置 */
    private _initContentPos() {
        const scroll = this.node.getComponent(ScrollView);
        const content = scroll.content.getComponent(UITransform);
        const view = content.node.parent.getComponent(UITransform);
        content.node.position = v3(0, (view.height / 2) - (content.height * (1 - content.anchorY)));
    }

    protected onSizeChange() {
        this._isResize = true;
        this._initCounter = 0;
        this._itemsUUIDToIndex = {};
        this._indexToItem = {};
        // this.itemList = [];
        if (!EDITOR)
            this._getContentUTF().node.removeAllChildren();
    }

    private _hideAllItems() {
        this.itemList.forEach((item: Node, index: number) => item.active = false);
    }

    /** 获取content */
    private _getContentUTF() {
        return this.node.getComponent(ScrollView).content.getComponent(UITransform);
    }

    /** 初始化 */
    private _initialize(force: boolean = false) {
        if (this._isInit && !force) return;
        const scroll = this.node.getComponent(ScrollView);
        scroll.enabled = false;
        this._isInit = true;
        const content = this._getContentUTF();
        this.content = content;
        if (!EDITOR)
            content.node.removeAllChildren()
        this.itemList = [];
        const viewUTF = content.node.parent.getComponent(UITransform);
        this._viewW = viewUTF.width;
        this._viewH = viewUTF.height;

        const itemData = this.itemPrefab.data.getComponent(UITransform);
        this._itemW = itemData.width * this.itemScale + this.spacing.x;
        this._itemH = itemData.height * this.itemScale + this.spacing.y;

        this._lastPosY = content.node.position.y;
        this._viewRow = Math.ceil((this._viewH - this.spacing.y) / this._itemH) + 1;
        this._viewCol = Math.floor((this._viewW + this.spacing.x) / this._itemW);
        const surplusW = this._viewW - (this._viewCol * this._itemW);
        const startPos = v2(
            (-this._viewW >> 1) + (this._itemW >> 1) + (surplusW >> 1),
            (-this._itemH >> 1) - this.paddingTop
        );
        this._itemStartPos = startPos;

        const cNum = this._viewRow * this._viewCol;
        log(`实例化数量:${cNum}`);

        let createNum = 0;
        const createFunc = (index: number) => {
            if (!isValid(content)) return; //异步创建，创建完回来父节点有可能已经被销毁
            const item = instantiate(this.itemPrefab);
            item.parent = content.node;
            const x = (index % this._viewCol) * this._itemW;
            const y = -Math.floor(index / this._viewCol) * this._itemH + (this.spacing.y >> 1);
            const pos = v3(x + startPos.x, y + startPos.y);
            item.setPosition(pos);
            item.setScale(v3(this.itemScale, this.itemScale, 1));
            item.on(Node.EventType.TOUCH_START, () => {
                this._isLongPress = false;
                if (this.onItemLongPress) {
                    this.longPressTimer = Number(setTimeout(() => {
                        this._isLongPress = true;
                        this.longPressTimer = 0;
                        this.onItemLongPress(this._itemsUUIDToIndex[item.uuid], item);
                    }, this.longPressTime * 1000))
                }
            })
            item.on(Node.EventType.TOUCH_MOVE, () => {
                if (this.longPressTimer) {
                    clearTimeout(this.longPressTimer);
                    this.longPressTimer = 0;
                }
            })
            item.on(Node.EventType.TOUCH_END, () => {
                if (this._isLongPress) return;
                if (this.longPressTimer) {
                    clearTimeout(this.longPressTimer);
                    this.longPressTimer = 0;
                }
                this.onItemClicked(this._itemsUUIDToIndex[item.uuid], item);

                if (this._isSelectable && this.onItemSelected) {
                    const lastSelectedIndex = this._selectedIndex;
                    this._selectedIndex = this._itemsUUIDToIndex[item.uuid];
                    this.onItemSelected(this._itemsUUIDToIndex[item.uuid], item);
                    const lastItem = this._indexToItem[lastSelectedIndex];
                    if (lastItem && !this._isMultiSelect) {
                        this.onItemSelected(lastSelectedIndex, lastItem);
                    }
                }

                if (this._isMultiSelect && this.onItemSelected) {
                    const selectedIndex = this._itemsUUIDToIndex[item.uuid];
                    if (this._selectedIndexes.indexOf(selectedIndex) == -1) {
                        this._selectedIndexes.push(selectedIndex);
                    } else {
                        this._selectedIndexes.splice(this._selectedIndexes.indexOf(selectedIndex), 1);
                    }
                    this.onItemSelected(selectedIndex, item);
                }
            }, this);

            this.itemList[index] = item;

            item["needRender"] = true;
            this._updateItem(index, item);

            this._itemsUUIDToIndex[item.uuid] = index;
            this._indexToItem[index] = item;

            createNum++;
            if (createNum == cNum) {
                scroll.enabled = true;

                director.emit('recycle_init_success');
            }
        }
        // createFlag++;
        /** 分帧创建item */
        // frameLoad(cNum, createFunc, 16, 0, 0);
        this.frameLoadParama = { fun: createFunc, count: cNum, index: 0 };
    }

    /** 更新centent高度 */
    private _updateContentHeight() {
        const content = this._getContentUTF();
        const col = Math.floor((this._viewW + this.spacing.x) / this._itemW);
        const row = Math.ceil(this.numItems / col);
        const itemH = this.itemPrefab.data.getComponent(UITransform).height * this.itemScale + this.spacing.y;
        content.height = (row * itemH) - (this.spacing.y) + (this.paddingTop * 2);
    }

    /** 获取item在view坐标系的对标 */
    private _getPosInView(item: Node) {
        const content = this._getContentUTF();
        const viewUTF = content.node.parent.getComponent(UITransform);
        const wpos = content.convertToWorldSpaceAR(item.position);
        const lpos = viewUTF.convertToNodeSpaceAR(wpos);
        return lpos;
    }

    /** 更新item */
    private _updateItem(index: number, item: Node, force = false) {
        const isShow = index >= 0 && index < this.numItems;
        item.active = isShow;
        if (isShow) {
            if (item["needRender"] || force) {
                this.onItemRender(index, item);
                item["needRender"] = false;

                if (this._isSelectable && this.onItemSelected) {
                    this.onItemSelected(index, item);
                }

                if (this._isMultiSelect && this.onItemSelected) {
                    this.onItemSelected(index, item);
                }
            }
        }
    }

    /**
     * 根据索引刷新单个Item
     * @param index 要刷新的Item索引
     */
    public refreshItem(index: number) {
        // 检查索引是否有效
        if (index < 0 || index >= this.numItems) {
            console.warn(`刷新Item失败: 无效的索引 ${index}`);
            return;
        }

        // 查找对应的Item节点
        const item = this.itemList.find(item => this._itemsUUIDToIndex[item.uuid] === index);

        if (item) {
            // 强制更新Item
            this._updateItem(index, item, true);
        } else {
            console.warn(`刷新Item失败: 未找到索引 ${index} 对应的Item节点`);
        }
    }

    /**
     * 刷新所有可见的Items
     */
    public refreshVisibleItems() {
        this.itemList.forEach(item => {
            const index = this._itemsUUIDToIndex[item.uuid];
            if (item.active) {
                this._updateItem(index, item, true);
            }
        });
    }

    public update(dt: number) {
        if (this.frameLoadParama?.count) {
            const fun = this.frameLoadParama.fun;
            fun && fun(this.frameLoadParama.index);
            this.frameLoadParama.index++;
            if (this.frameLoadParama.index >= this.frameLoadParama.count) {
                this.frameLoadParama = null;
            }
        }
        if (this._isResize) {
            this._initCounter += dt;
            if (this._initCounter >= this._initTimer) {
                this._isInit = false;
                this._isResize = false;
                this.numItems = this._numItems;
                this._isResizeFinish = true;
            }
            return;
        }
        const content = this._getContentUTF();
        const currY = content.node.position.y;
        const dtY = currY - this._lastPosY;
        this._lastPosY = currY;
        this._fleshCounter += dt;
        if (dtY == 0 && !this._isResizeFinish) return;
        const isDown = dtY < 0;
        const viewHalfH = this._viewH >> 1;
        const itemHalfH = this._itemH >> 1;
        const lineIndex = Math.floor((currY - viewHalfH + (this.spacing.y >> 1) - this.paddingTop) / (this._itemH));
        let isLineChange = this._lineIndex != lineIndex;

        if (!isLineChange && !this._isResizeFinish) return;
        this._isResizeFinish = false;
        this._lineIndex = lineIndex;
        const pageHeight = this._itemH * this._viewRow;
        const pageLen = this._viewRow * this._viewCol;
        const pageIndex = Math.floor((currY - viewHalfH - this.paddingTop) / pageHeight);
        const itemsLen = this.itemList.length;
        for (let i = 0; i < itemsLen; ++i) {
            const index = i;
            const item = this.itemList[i];
            const x = (index % this._viewCol) * this._itemW;
            const y = -Math.floor(index / this._viewCol) * this._itemH + (this.spacing.y >> 1);
            const pos = v3(x + this._itemStartPos.x, y + this._itemStartPos.y - pageIndex * (pageHeight));
            item.setPosition(pos);

            const posInView = this._getPosInView(item);
            const lastIndex = this._itemsUUIDToIndex[item.uuid];
            let currIndex = pageIndex * pageLen + i;
            if (!isDown) {
                if (posInView.y >= (viewHalfH + itemHalfH)) {
                    item.setPosition(v3(item.position.x, item.position.y - (this._viewRow * this._itemH)));
                    currIndex += itemsLen;
                }
            } else {
                if (posInView.y >= viewHalfH + itemHalfH) {
                    item.setPosition(v3(item.position.x, item.position.y - (this._viewRow * this._itemH)));
                    currIndex += itemsLen;
                }
                if (isLineChange) {
                    const posInView = this._getPosInView(item);
                    if (posInView.y <= -(viewHalfH + itemHalfH)) {
                        item.setPosition(v3(item.position.x, item.position.y + (this._viewRow * this._itemH)));
                        currIndex -= itemsLen;
                    }
                }
            }
            if (currIndex != lastIndex) {
                item["needRender"] = true;
                this._updateItem(currIndex, item);
            }
            this._itemsUUIDToIndex[item.uuid] = currIndex;

            this._indexToItem[currIndex] = item;
        }
    }

    /** 获取item */
    getItemByIndex(index: number): Node | undefined {
        if (index < 0 || index >= this.numItems) {
            return undefined;
        }

        let uiid = ``;
        for (const key in this._itemsUUIDToIndex) {
            if (this._itemsUUIDToIndex[key] == index) {
                uiid = key;
                break;
            }
        }

        for (const node of this.itemList) {
            if (node.uuid == uiid) {
                return node;
            }
        }

        return undefined;
    }

    /** 强制显示指定索引的Item
     * @param index 索引
     */
    forcedDisplayItemByIndex(index: number) {
        // 获取滚动视图组件
        const scrollView = this.getComponent(ScrollView);
        if (!scrollView) {
            error("RecycleScroll: 找不到 ScrollView 组件");
            return;
        }

        //停止滑动滑动视图的滑动和惯性滑动
        scrollView.stopAutoScroll();
        // 检查索引是否有效
        if (index < 0 || index >= this._numItems) {
            error("RecycleScroll: 无效的索引值");
            return;
        }

        // 计算目标位置
        const content = scrollView.content;
        const viewHeight = this.node.getComponent(UITransform).height;

        const contentHeight = content.getComponent(UITransform).height;
        const contentAnchor = content.getComponent(UITransform).anchorPoint;


        // 处理ScrollView锚点问题
        const scrollViewAnchor = scrollView.node.getComponent(UITransform).anchorPoint;
        let initPosY = 0 + scrollViewAnchor.y * viewHeight;

        // 计算目标行数和目标Y坐标
        const targetRow = Math.floor(index / this._viewCol);
        let targetY = targetRow * this._itemH + initPosY;

        content.setPosition(v3(content.position.x, targetY, content.position.z));

        // 计算content底部的y坐标
        const contentBottomY = content.worldPosition.y - contentHeight * contentAnchor.y;
        //计算scrollview底部的坐标
        const scrollViewBottomY = this.node.worldPosition.y - viewHeight * scrollViewAnchor.y;

        //如果content底部超出scrollview底部,则将content位置调整到底部对齐
        if (contentBottomY > scrollViewBottomY) {
            content.setPosition(v3(content.position.x, targetY - (contentBottomY - scrollViewBottomY), content.position.z));
        }

        // 强制更新一次显示
        this.updateAllItems();
    }

    /** 检查索引是否正在渲染
     * @param index 
     * @returns 
     */
    isRanderingItemByIndex(index: number) {
        // 检查索引是否有效
        if (index < 0 || index >= this._numItems) {
            return false;
        }

        for (const key in this._itemsUUIDToIndex) {
            if (this._itemsUUIDToIndex[key] == index) {
                return true;
            }
        }

        return false;
    }
}